
#include "include/types.h"
// #include "include/stat.h"
#include "include/param.h"
#include "include/memlayout.h"
#include "include/riscv.h"
#include "include/spinlock.h"
#include "include/proc.h"
#include "include/syscall.h"
#include "include/defs.h"
#include "include/sysinfo.h"
#include "include/uname.h"
#include "include/times.h"
#include "include/debug.h"
#include "include/sysnum.h"
#include "include/errno.h"
// Fetch the uint64 at addr from the current process.
int
fetchaddr(uint64 addr, uint64 *ip)
{
  struct proc *p = myproc();
  if(addr >= p->sz || addr+sizeof(uint64) > p->sz)
    return -1;
  if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0)
    return -1;
  return 0;
}

// Fetch the nul-terminated string at addr from the current process.
// Returns length of string, not including nul, or -1 for error.
int
fetchstr(uint64 addr, char *buf, int max) // addr是第n+1个参数的用户空间的地址，
{
  struct proc *p = myproc();
  int err = copyinstr(p->pagetable, buf, addr, max); // 从用户空间拷贝到内核空间
  if(err < 0)
    return err;
  return strlen(buf);
}

static uint64
argraw(int n)
{
  struct proc *p = myproc();
  switch (n) {
  case 0:
    return p->trapframe->a0;
  case 1:
    return p->trapframe->a1;
  case 2:
    return p->trapframe->a2;
  case 3:
    return p->trapframe->a3;
  case 4:
    return p->trapframe->a4;
  case 5:
    return p->trapframe->a5;
  }
  panic("argraw");
  return -1;
}

// Fetch the nth 32-bit system call argument.
int
argint(int n, int *ip)
{
  *ip = argraw(n);
  return 0;
}

// Retrieve an argument as a pointer.
// Doesn't check for legality, since
// copyin/copyout will do that.
int
argaddr(int n, uint64 *ip)
{
  *ip = argraw(n); // 根据n，拿到第n+1个参数，n=0时是第1个参数
  return 0;
}

// Fetch the nth word-sized system call argument as a null-terminated string.
// Copies into buf, at most max.
// Returns string length if OK (including nul), -1 if error.
int
argstr(int n, char *buf, int max)  // argstr(0, path, FAT32_MAX_PATH)
{
  uint64 addr;
  if(argaddr(n, &addr) < 0) // *addr = 第n+1个参数的值，所以addr是第n+1个参数的地址，对于mkdir是第一个参数的值，也就是path的指针
    return -1;
  return fetchstr(addr, buf, max); // addr是第n+1个参数用户空间的地址，buf是内核空间复制过来的参数的地址。
}

extern uint64 sys_chdir(void);
extern uint64 sys_close(void);
extern uint64 sys_dup(void);
extern uint64 sys_exec(void);
extern uint64 sys_exit(void);
extern uint64 sys_fork(void);
extern uint64 sys_fstat_xv6(void);
extern uint64 sys_getpid(void);
extern uint64 sys_getppid(void);
extern uint64 sys_kill(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_open(void);
extern uint64 sys_openat(void);
extern uint64 sys_pipe(void);
extern uint64 sys_read(void);
extern uint64 sys_sbrk(void);
extern uint64 sys_sleep(void);
extern uint64 sys_wait(void);
extern uint64 sys_write(void);
extern uint64 sys_uptime(void);
extern uint64 sys_test_proc(void);
extern uint64 sys_dev(void);
extern uint64 sys_dir(void);
extern uint64 sys_getcwd(void);
extern uint64 sys_remove(void);
extern uint64 sys_trace(void);
extern uint64 sys_sysinfo(void);
extern uint64 sys_clone(void);
extern uint64 sys_uname(void);
extern uint64 sys_times(void);
extern uint64 sys_gettimeofday(void);
extern uint64 sys_nanosleep(void);
extern uint64 sys_brk(void);
extern uint64 sys_dup3(void);
extern uint64 sys_fstat(void);
extern uint64 sys_getdents64(void);
extern uint64 sys_sched_yield(void);
extern uint64 sys_unlinkat(void);
extern uint64 sys_mmap(void);
extern uint64 sys_munmap(void);
extern uint64 sys_waitpid(void);
extern uint64 sys_mount(void);
extern uint64 sys_umount2(void);
extern uint64 sys_getuid(void);
extern uint64 sys_geteuid(void);
extern uint64 sys_getgid(void);
extern uint64 sys_getegid(void);
extern uint64 sys_set_tid_address(void);
extern uint64 sys_readlinkat(void);
extern uint64 sys_writev(void);
extern uint64 sys_rt_sigprocmask(void);
extern uint64 sys_gettid(void);
extern uint64 sys_rt_sigaction(void);
extern uint64 sys_tg_kill(void);
extern uint64 sys_ioctl(void);
extern uint64 sys_getpgid(void);
extern uint64 sys_ppoll(void);
extern uint64 sys_setpgid(void);
extern uint64 sys_fstatat(void);
extern uint64 sys_clock_gettime(void);
extern uint64 sys_syslog(void);
extern uint64 sys_utimensat(void);
extern uint64 sys_sendfile(void);
extern uint64 sys_fcntl(void);
static uint64 (*syscalls[])(void) = {
  [SYS_fork]    sys_fork,
  [SYS_exit]    sys_exit,
  [SYS_wait]    sys_wait,
  [SYS_pipe]    sys_pipe,
  [SYS_read]    sys_read,
  [SYS_kill]    sys_kill,
  [SYS_exec]    sys_exec,
  [SYS_fstat]   sys_fstat_xv6,
  [SYS_chdir]   sys_chdir,
  [SYS_dup]     sys_dup,
  [SYS_getpid]  sys_getpid,
  [SYS_sbrk]    sys_sbrk,
  [SYS_sleep]   sys_sleep,
  [SYS_uptime]  sys_uptime,
  [SYS_open]    sys_open,
  [SYS_write]   sys_write,
  [SYS_mkdir]   sys_mkdir,
  [SYS_close]   sys_close,
  [SYS_test_proc]    sys_test_proc,
  [SYS_dev]     sys_dev,
  [SYS_dir]     sys_dir,
  [SYS_remove]  sys_remove,
  [SYS_trace]   sys_trace,
  [SYS_sysinfo] sys_sysinfo,
  /* Following are added by OUR GROUP */
  [__NR_fcntl]    sys_fcntl,
  [__NR_openat]   sys_openat,
  [__NR_read]     sys_read,
  [__NR_close]    sys_close,
  [__NR_mkdirat]  sys_mkdir,
  [__NR_getcwd]   sys_getcwd,
  [__NR_write]    sys_write,
  [__NR_chdir]    sys_chdir,
  [__NR_getpid]   sys_getpid,
  [__NR_getppid]  sys_getppid,
  [__NR_clone]    sys_clone, 
  [__NR_wait4]    sys_waitpid,
  [__NR_exit]     sys_exit,
  [__NR_execve]   sys_exec,
  [__NR_pipe2]    sys_pipe,
  [__NR_uname]    sys_uname,
  [__NR_times]    sys_times,
  [__NR_brk]      sys_brk, 
  [__NR_dup]      sys_dup,
  [__NR_dup3]     sys_dup3,
  [__NR_fstat]    sys_fstat,
  [__NR_ioctl]    sys_ioctl,
  [__NR_getpgid]  sys_getpgid,
  [__NR_ppoll]    sys_ppoll,
  [__NR_setpgid]  sys_setpgid,
  [__NR_fstatat]  sys_fstatat,
  [__NR_mmap]     sys_mmap,
  [__NR_munmap]   sys_munmap,
  [__NR_mount]    sys_mount,
  [__NR_umount2]  sys_umount2,
  [__NR_getuid]   sys_getuid,
  [__NR_geteuid]  sys_geteuid,
  [__NR_getgid]   sys_getgid,
  [__NR_getegid]  sys_getegid,
  [__NR_unlinkat] sys_unlinkat,
  [__NR_writev]   sys_writev,
  [__NR_gettid]   sys_gettid,
  [__NR_tgkill]   sys_tg_kill,
  [__NR_syslog]   sys_syslog,
  [__NR_sendfile] sys_sendfile,
  [__NR_utimensat]    sys_utimensat,
  [__NR_readlinkat]   sys_readlinkat,
  [__NR_exit_group]   sys_exit, // TODO Fix exit_group
  [__NR_gettimeofday] sys_gettimeofday,
  [__NR_nanosleep]    sys_nanosleep,
  [__NR_getdents64]   sys_getdents64,
  [__NR_sched_yield]  sys_sched_yield,
  [__NR_rt_sigaction] sys_rt_sigaction,
  [__NR_clock_gettime]    sys_clock_gettime,
  [__NR_rt_sigprocmask]   sys_rt_sigprocmask,
  [__NR_set_tid_address]  sys_set_tid_address,
};

void
syscall(void)
{
  static char *sysnames[] = {
  [SYS_fork]    "sys_fork",
  [SYS_exit]    "sys_exit",
  [SYS_wait]    "sys_wait",
  [SYS_pipe]    "sys_pipe",
  [SYS_read]    "sys_read",
  [SYS_kill]    "sys_kill",
  [SYS_exec]    "sys_exec",
  [SYS_fstat]   "sys_fstat",
  [SYS_chdir]   "sys_chdir",
  [SYS_dup]     "sys_dup",
  [SYS_getpid]  "sys_getpid",
  [SYS_sbrk]    "sys_sbrk",
  [SYS_sleep]   "sys_sleep",
  [SYS_uptime]  "sys_uptime",
  [SYS_open]    "sys_open",
  [SYS_write]   "sys_write",
  [SYS_mkdir]   "sys_mkdir",
  [SYS_close]   "sys_close",
  [SYS_test_proc]    "sys_test_proc",
  [SYS_dev]     "sys_dev",
  [SYS_dir]     "sys_dir",
  [SYS_remove]  "sys_remove",
  [SYS_trace]   "sys_trace",
  [SYS_sysinfo] "sys_sysinfo",
  // following are added by myself
  [__NR_fcntl]    "sys_fcntl",
  [__NR_openat]  "sys_openat",
  [__NR_read]  "sys_read",
  [__NR_close]  "sys_close",
  [__NR_mkdirat]  "sys_mkdir",
  [__NR_getcwd]  "sys_getcwd",
  [__NR_write]  "sys_write",
  [__NR_chdir]  "sys_chdir",
  [__NR_getpid] "sys_getpid",
  [__NR_getppid] "sys_getppid",
  [__NR_clone] "sys_clone",
  [__NR_wait4] "sys_waitpid",
  [__NR_exit]  "sys_exit",
  [__NR_execve] "sys_exec",
  [__NR_pipe2]  "sys_pipe",
  [__NR_uname] "sys_uname",
  [__NR_times] "sys_times",
  [__NR_gettimeofday] "sys_gettimeofday",
  [__NR_nanosleep] "sys_nanosleep",
  [__NR_brk] "sys_brk", 
  [__NR_dup]  "sys_dup",
  [__NR_dup3]  "sys_dup3",
  [__NR_fstat]  "sys_fstat",
  [__NR_getdents64]  "sys_getdents64",
  [__NR_sched_yield] "sys_sched_yield",
  [__NR_unlinkat]  "sys_unlinkat",
  [__NR_mmap] "sys_mmap",
  [__NR_munmap] "sys_munmap",
  [__NR_mount]  "sys_mount",
  [__NR_umount2]  "sys_umount2",
  [__NR_getuid] "sys_getuid",
  [__NR_geteuid] "sys_geteuid",
  [__NR_getgid] "sys_getgid",
  [__NR_getegid] "sys_getegid",
  [__NR_set_tid_address]  "sys_set_tid_address",
  [__NR_readlinkat]  "sys_readlinkat",
  [__NR_writev]  "sys_writev",
  [__NR_rt_sigprocmask] "sys_rt_sigprocmask",
  [__NR_gettid] "sys_gettid",
  [__NR_rt_sigaction] "sys_rt_sigaction",
  [__NR_tgkill] "sys_tg_kill",
  [__NR_exit_group]  "sys_exit",
  [__NR_ioctl]  "sys_ioctl",
  [__NR_getpgid] "sys_getpgid",
  [__NR_ppoll]  "sys_ppoll",
  [__NR_setpgid] "sys_setpgid",
  [__NR_fstatat]  "sys_fstatat",
  [__NR_clock_gettime] "sys_clock_gettime",
  [__NR_syslog]     "sys_syslog",
  [__NR_utimensat]  "sys_utimensat",
  [__NR_sendfile] "sys_sendfile",
  };
  int num;
  struct proc *p = myproc();
  num = p->trapframe->a7;
  if(num > 0 && syscalls[num]) {
    if (num != 0) {
      __debug_info("syscall", "syscall num = %d\n", num);
      __debug_info("syscall", "syscall name = %s\n", sysnames[num]);
      __debug_info("syscall", "epc = 0x%x\n", r_sepc());
    }
    p->trapframe->a0 = syscalls[num]();
    if (num != 0) {
      __debug_info("syscall", "return value = %d = 0x%x\n\n", p->trapframe->a0, p->trapframe->a0);
    }
        // trace
    if ((p->tmask & (1 << num)) != 0) {
      printf("pid %d: syscall %s -> %d\n", p->pid, sysnames[num], p->trapframe->a0);
    }
  } else {
    printf("pid %d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    __debug_info("syscall", "epc = 0x%x\n", r_sepc());
    p->trapframe->a0 = -1;
  }
}

uint64 
sys_syslog(void)
{
  __debug_error("syslog", "not implet\n");
  return 0;
}

uint64 
sys_ppoll(void)
{
  return 1;
}

uint64 
sys_ioctl(void)
{
  return 0;
}

uint64 
sys_tg_kill(void)
{
  return 0;
}

uint64 
sys_uname(void)
{
  uint64 addr;
  struct proc *p = myproc();
  if (argaddr(0, &addr) < 0) {
    return -1;
  }
  // TODO explain why release number matters
	static struct utsname thisname = {
		"xv6", "debian", "5.0", "#488-Microsoft", "QEMU emulator version 4.2.1", "NIS domain name"
	};
  if (copyout(p->pagetable, addr, (char *)&thisname, sizeof(thisname)) < 0) {
    return -1;
  }
	return 0;

}
uint64 
sys_test_proc(void)
{
  int n;
  argint(0, &n);
  printf("hello world from proc %d, hart %d, arg %d\n", myproc()->pid, r_tp(), n);
  return 0;
}

uint64
sys_sysinfo(void)
{
  uint64 addr;
  struct proc *p = myproc();

  if (argaddr(0, &addr) < 0) {
    return -1;
  }

  struct sysinfo info;
  info.freemem = freemem_amount();
  info.nproc = procnum();

  if (copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0) {
    return -1;
  }

  return 0;
}
